/**
 * Copyright 2011-2011 - Members of the Chaupal Community
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package net.chaupal.Impl.v1.Proof.ConfirmationProtocol;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.TreeSet;

public final class CPTool {

    public static void printStatus(HashMap<String, CPAnalysis> inBase) {
        
        int converges = 0;
        int hasEquivalence = 0;

        for (CPAnalysis item : inBase.values()) {

            if (item.isCoherentConvergence()) {
                converges += 1;
            }

            if (item.getEquivalence() != null) {
                hasEquivalence += 1;
            }

        }

        System.out.println("Converges       = " + converges);
        System.out.println("Has equivalence = " + hasEquivalence);
        System.out.println("Total sequences = " + inBase.size() + "\n");
        
    }

    private static File seqFile;
    private static FileWriter seqFW;

    @SuppressWarnings("NestedAssignment")
    public static void main(String[] args) throws FileNotFoundException, IOException {

        try {
            seqFile = new File("Result.txt");
            if ( seqFile.exists() ) {
                seqFile.delete();
            }
            seqFW = new FileWriter(seqFile);
        } catch (IOException ex) {
            System.err.println(ex);
        }

        CombinedSequences cseqs = new CombinedSequences();
        cseqs.load();
        HashMap<String, CPAnalysis> base = cseqs.getAnalysisBase();

        // Default converging sequences
        base.get("A1I").setCoherentConvergence(true);
        // base.get("A1N").setCoherentConvergence(true);
        base.get("A1L").setCoherentConvergence(true);

        // Default converging sequences
        base.get("A1I").setConfirmed(false);
        // base.get("A1N").setConfirmed(false);
        base.get("A1L").setConfirmed(false);

        base.get("A2I").setDisfunctional(true);
        base.get("A2L").setDisfunctional(true);
        // base.get("A2N").setDisfunctional(true);

        base.get("B1I").setDisfunctional(true);
        base.get("B1L").setDisfunctional(true);
        // base.get("B1N").setDisfunctional(true);

        // Checking for dysfunctional sequences
        for (CPAnalysis c : base.values()) {

            // Sequences with three A1's are disfunctional
            int countA1 = 0;
            int countA2 = 0;
            int countB1 = 0;
            int countN = 0;
            int countL = 0;
            int countI = 0;
            int idx = 0;

            while ((idx = c.getShortCode().indexOf("A1", idx)) != -1) {
                idx++; countA1++;
            }

            while ((idx = c.getShortCode().indexOf("A2", idx)) != -1) {
                idx++; countA2++;
            }

            while ((idx = c.getShortCode().indexOf("B1", idx)) != -1) {
                idx++; countB1++;
            }

            while ((idx = c.getShortCode().indexOf('I', idx)) != -1) {
                idx++; countI++;
            }

            while ((idx = c.getShortCode().indexOf('L', idx)) != -1) {
                idx++; countL++;
            }

            while ((idx = c.getShortCode().indexOf('N', idx)) != -1) {
                idx++; countN++;
            }

            // More than two A1 is dysfunctional
            if ( countA1 > 2 ) {
                c.setDisfunctional(true);
            }

            // Sequences with two A2's or more are disfunctional
            if ( countA2 > 1 ) {
                c.setDisfunctional(true);
            }

            // Sequences with three or more B1's are disfunctional
            if ( countB1 > 2 ) {
                c.setDisfunctional(true);
            }

            // Sequences with two B1's and no A2 are disfunctional
            if ( ( countB1 == 2 ) && ( countA2 == 0) ) {
                c.setDisfunctional(true);
            }

            // A2 sent before A1 is dysfunctional
            int f1st = c.getShortCode().indexOf("A2");
            int s2nd = c.getShortCode().indexOf("A1");
            if ( ( f1st > 0 ) && ( s2nd > 0 ) ) {
                if ( f1st < s2nd ) {
                    c.setDisfunctional(true);
                }
            }

            // B1 sent before A1 is dysfunctional
            int ff1st = c.getShortCode().indexOf("B1");
            int ss2nd = c.getShortCode().indexOf("A1");
            if ( ( ff1st > 0 ) && ( ss2nd > 0 ) ) {
                if ( ff1st < ss2nd ) {
                    c.setDisfunctional(true);
                }
            }

            // Sequences starting with B1 are dysfuntional
            if ( ff1st == 0 ) {
                c.setDisfunctional(true);
            }

            // Sequences beginning with A2 are dysfunctional
            if ( c.getShortCode().startsWith("A2") ) {
                c.setDisfunctional(true);
            }

            if ( c.getShortCode().length() == 6 ) {
                if ( c.getShortCode().startsWith("A1IB1I") ) {
                    c.setCoherentConvergence(true);
                    c.setConfirmed(true);
                } else {
                    c.setCoherentConvergence(true);
                    c.setConfirmed(false);
                }
            }

            // Sequences with one A1I and no B1I
            int countA1I = 0; int countB1I = 0;

            while ((idx = c.getShortCode().indexOf("A1I", idx)) != -1) {
                idx++; countA1I++;
            }

            while ((idx = c.getShortCode().indexOf("B1I", idx)) != -1) {
                idx++; countB1I++;
            }

            if ( ( countA1I == 1 ) && ( countB1I == 0 ) ) {
                c.setCoherentConvergence(true);
                c.setConfirmed(false);
            }

            // If only Bob communicates, the sequence converges
            if ( c.getShortCode().indexOf('A') == - 1) {
                c.setDisfunctional(true);
            }

        }

        // Default converging sequences
        for (CPAnalysis c : base.values()) {

            // If only Alice communicates, the sequence converges
            if ( c.getShortCode().indexOf('B') == - 1) {
                c.setCoherentConvergence(true);
                c.setConfirmed(false);
            }

            // If confirmation follows message in time, the sequence converges
            // subsequent messages are ignored
            if ( c.getShortCode().startsWith("A1IB1I") ) {
                c.setCoherentConvergence(true);
                c.setConfirmed(true);
            }

            // If confirmation follows message in time, regardless of A2,
            // the sequence converges
            if ( c.getShortCode().startsWith("A1IA2IB1I") ) {
                c.setCoherentConvergence(true);
                c.setConfirmed(true);
            }

            int Is = c.getShortCode().indexOf('I');
            int Ns = c.getShortCode().indexOf('N');

            if ( ( Is < 0 ) && ( Ns < 0 ) ) {
                c.setCoherentConvergence(true);
                c.setConfirmed(false);
            }

        }

        for (CPAnalysis c : base.values()) {

            if ( !c.isDisfunctional() ) {

                String code = c.getShortCode();

                String equi = findEquivalence(code);
                if ( !code.equals(equi) ) {
                    CPAnalysis x = base.get(equi);
                    c.setEquivalence(x);
                }

                String equi2 = findEquivalence2(code);
                if ( !code.equals(equi2) ) {
                    CPAnalysis x = base.get(equi2);
                    c.setEquivalence(x);
                }

                if ( code.length() > 3 ) {

                    // Alice ignores early confirmations
                    if ( code.startsWith("B1I") ) {
                        c.setEquivalence(base.get(code.substring(3)));
                    }

                }

                if ( code.length() > 6 ) {

                    // Bob ignore the second reception of A1
                    if ( code.startsWith("A1IA1I") ) {
                        c.setEquivalence(base.get(code.substring(3)));
                    }

                }

                if ( code.length() > 3 ) {
                    if ( code.endsWith("B1L") ) {
                        c.setEquivalence(base.get(code.substring(0, code.length()-3)));
                    }
                }

                if ( code.startsWith("A1IA2IA1IB1I") ) {
                    c.setConfirmed(true);
                    c.setCoherentConvergence(true);
                }

            }

        }

        System.out.println("After processing more converging sequences");
        printStatus(base);

        // Simplify
        boolean loopAgain;
        do {
            // System.out.println("Beep");
            loopAgain = false;
            for (CPAnalysis c : base.values()) {
                CPAnalysis x = c.getEquivalence();
                if ( x != null ) {
                    CPAnalysis y = x.getEquivalence();
                    if ( y != null ) {
                        c.setEquivalence(y);
                        loopAgain = true;
                    }
                }
            }
        } while (loopAgain);

        System.out.println("After processing transitive convergence");
        printStatus(base);

        // Printing analysis result
        int notOK = 0;
        for (CPAnalysis c : base.values()) {
            if ( !c.isDisfunctional() ) {
                if ( !c.isCoherentConvergence() ) {
                    notOK++;
                    System.out.println(
                        c.getShortCode() +
                        " -> " + c.getEquivalence() +
                        " - Confirmed: " + c.isConfirmed() +
                        " - Disfunctional: " + c.isDisfunctional()
                    );
                }
            }

            if ( c.isDisfunctional() ) {
                // seqFW.write(c.getShortCode() + " -> Dysfunctional\n");
            } else {
                seqFW.write(
                    c.getShortCode() +
                    " -> " + c.getEquivalence() +
                    " - Confirmed: " + c.isConfirmed() + "\n");
            }

        }

        System.out.println("Not OK: " + notOK);

        try {
            seqFW.close();
        } catch (IOException ex) {
            System.err.println(ex);
        }

    }

    public static String findEquivalence2(String shortCode) {
        
        if ( shortCode == null ) return null;

        ArrayList<String> split = new ArrayList<String>();
        ArrayList<String> Is = new ArrayList<String>();
        ArrayList<String> Ls = new ArrayList<String>();
        StringBuilder sb = new StringBuilder();

        for (int i=0;i<(shortCode.length()/3);i++) {
            int pos = i*3;
            int end = Math.min(pos+3, shortCode.length());
            if ( pos < end ) {
                split.add(shortCode.substring(pos, end));
            }
        }

        for (String item : split.toArray(new String[0])) {
            if ( item.indexOf('L') > 0 ) {
                Ls.add(item);
            } else {
                Is.add(item);
            }
        }

        for (String item : Is.toArray(new String[0])) {
            sb.append(item);
        }

        for (String item : Ls.toArray(new String[0])) {
            sb.append(item);
        }

        return sb.toString();

    }

    
    public static String findEquivalence(String shortCode) {
        if ( shortCode == null ) return null;
        String result = remove(shortCode, "A1N");
        if ( result != null ) return result;
        result = remove(shortCode, "A2N");
        if ( result != null ) return result;
        return remove(shortCode, "B1N");
    }

    public static String remove(String shortCode, String toRemove) {
        if ( toRemove == null || shortCode == null ) return null;
        String result = null;
        if ( shortCode.length() >= 3 ) {
            int pos = shortCode.indexOf(toRemove);
            if ( pos != -1 ) {
                result = shortCode.substring(0, pos);
                if ( (pos+3) < shortCode.length() ) {
                    result += shortCode.substring((pos+3), shortCode.length());
                }
            }
        }
        return result;
    }

    public static void Processing1() {

        CombinedSequences cseqs = new CombinedSequences();
        cseqs.generateSequences();

        ArrayList<CPSequence> seqs = cseqs.getCombinedSequences();

        TreeSet<String> keys = new TreeSet<String>();

        for (CPSequence item : seqs) {
            // System.out.println(item.getShortCode());
            keys.add(item.getShortCode());
        }

        StringBuilder tmp = new StringBuilder();
        Iterator<String> iter = keys.iterator();
        int i = 0;
        while (iter.hasNext()) {
            tmp.append(iter.next()).append(" ");
            i += 1;
            if ( i%4 == 0) tmp.append("\n");
        }

        System.out.println(tmp);
        System.out.println("\nCount: " + seqs.size());

    }

}
